﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Apewer.Web
{

    /// <summary></summary>
    public sealed class ApiApplication : IToJson
    {

        #region fields

        Type _type = null;
        string _module = null;

        // ApiAttribute
        string _name = null;
        string _caption = null;
        string _description = null;

        // invoke & enumerate
        bool _independent = false;
        bool _hidden = false;

        // functions
        Dictionary<string, ApiFunction> _dict = new Dictionary<string, ApiFunction>();
        List<ApiFunction> _list = new List<ApiFunction>();

        #endregion

        #region properties

        /// <summary></summary>
        public Type Type { get => _type; }

        /// <summary></summary>
        public string Module { get => _module; }

        /// <summary></summary>
        public string Name { get => _name; }

        /// <summary></summary>
        public string Caption { get => _caption; }

        /// <summary></summary>
        public string Description { get => _description; }

        /// <summary></summary>
        public bool Independent { get => _independent; }

        /// <summary></summary>
        public bool Hidden { get => _hidden; }

        /// <summary></summary>
        public ApiFunction[] Functions { get => _list.ToArray(); }

        #endregion

        /// <summary></summary>
        public ApiApplication(Type type, ApiAttribute api)
        {
            // type
            _type = type;

            // api
            if (api == null)
            {
                _name = type?.Name;
            }
            else
            {
                _name = string.IsNullOrEmpty(api.Name) ? type?.Name : api.Name;
                _caption = api.Caption;
                _description = api.Description;
            }

            if (type != null)
            {
                // caption
                if (string.IsNullOrEmpty(_caption))
                {
                    var captions = type.GetCustomAttributes(typeof(CaptionAttribute), true);
                    if (captions.Length > 0)
                    {
                        var caption = (CaptionAttribute)captions[0];
                        _caption = caption.Title;
                        if (string.IsNullOrEmpty(_description))
                        {
                            _description = caption.Description;
                        }
                    }
                }

                // hidden
                if (type.Contains<HiddenAttribute>(false)) _hidden = true;

                // independent
                if (type.Contains<IndependentAttribute>(false)) _independent = true;

                // Module
                var assemblyName = type.Assembly.GetName();
                _module = TextUtility.Join("-", assemblyName.Name, assemblyName.Version.ToString());

                // functions
                var funcs = new Dictionary<string, ApiFunction>();
                var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
                foreach (var method in methods)
                {
                    var func = ApiFunction.Parse(this, method);
                    if (func == null) continue;

                    var funcKey = func.Name.Lower();
                    if (funcs.ContainsKey(funcKey)) continue;
                    funcs.Add(funcKey, func);
                }
                _dict = funcs;

                _list.AddRange(funcs.Values);
                _list.Sort(new Comparison<ApiFunction>((a, b) => a.Name.CompareTo(b.Name)));
            }
        }

        internal ApiFunction GetFunction(string name)
        {
            if (string.IsNullOrEmpty(name)) return null;
            if (_dict.TryGetValue(name.ToLower(), out var func)) return func;
            return null;
        }

        /// <summary></summary>
        public Json ToJson() => ToJson(new ApiOptions());

        internal Json ToJson(ApiOptions options)
        {
            if (Hidden) return null;

            var json = Json.NewObject();
            json.SetProperty("name", _name);
            if (!string.IsNullOrEmpty(_caption)) json.SetProperty("caption", _caption);
            if (!string.IsNullOrEmpty(_description)) json.SetProperty("description", _description);
            if (options != null)
            {
                if (options.WithTypeName) json.SetProperty("type", _type.FullName);
                if (options.WithModuleName) json.SetProperty("mudule", _module);
                if (options.AllowEnumerate) json.SetProperty("functions", Json.From(_list));
            }
            return json;
        }

        /// <summary>解析类型，获取 <see cref="ApiApplication"/> 实例。</summary>
        /// <param name="type">要解析的类型。</param>
        /// <param name="requireAttribute">要求此类型拥有 Api 特性。</param>
        /// <returns>解析成功返回实例，解析失败返回 NULL 值。</returns>
        public static ApiApplication Parse(Type type, bool requireAttribute)
        {
            if (type == null) return null;

            // 检查类型的属性。
            if (!type.IsClass) return null;
            if (type.IsAbstract) return null;
            if (type.IsGenericType) return null;
            if (type.GetGenericArguments().NotEmpty()) return null;
            if (!RuntimeUtility.CanNew(type)) return null;

            // 判断基类。
            if (!typeof(ApiController).IsAssignableFrom(type)) return null;

            // 检查类型的特性。
            var apis = type.GetCustomAttributes(typeof(ApiAttribute), false);
            var api = apis.Length > 0 ? (ApiAttribute)apis[0] : null;
            if (requireAttribute && api == null) return null;

            return new ApiApplication(type, api);
        }

    }

}
